前言
上一章讲了使用 LWRP 的 Renderer Feature 来实现遮挡显示轮廓的效果,这节课对应着来用 Shader 实现,让我们看一下具体 Renderer Feature 完成了 Shader 中的哪些操作
另外,和上一节的效果相同,存在同一个BUG,使用同一个遮挡显示效果的 GameObject A与B,当都被遮挡住时,两者的轮廓不一定是A在前还是B在前,因为同一队列的两个物体不一定是谁先被渲染,目前没有找到控制该顺序的较好方法。
步骤
编写 “Custom/Shield/ShieldWhite” Pass,用于渲染遮挡后的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46```Shader "Custom/Shield"
{
SubShader
{
Tags{ "Queue" = "Geometry+200"}
Pass
{
Name "ShieldWhite"
Tags { "LightMode" = "ForwardBase" }
ZTest Greater
ZWrite off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos: SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag(v2f i) : SV_Target
{
return float4(1,1,1,1);
}
ENDCG
}
}
Fallback off
}
选择一个平常的 Shader(这里采用的是简单带光照的一个卡通Shader),并在 SubShader 的第一个 Pass 前调用 “Custom/Shield/ShieldWhite” ,并设置队列使其在大多数不透明物体之后渲染
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72Shader "Master/Toon"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1) // 基础色值
}
SubShader
{
Tags { "LightMode" = "ForwardBase" "Queue" = "Geometry+200" }
//重点!!!
UsePass "Custom/Shield/ShieldWhite"
Pass
{
Tags { "LightMode" = "ForwardBase" }
ZTest on
ZWrite on
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos: SV_POSITION;
float3 color : Color;
};
float4 _Diffuse;
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
o.color = ambient + diffuse;
return o;
}
float4 frag(v2f i) : SV_Target
{
return fixed4(i.color,1.0);
}
ENDCG
}
}
Fallback off
}
- 然后创建两个 材质,把 Shader 挂上去,分别调整下颜色作为区分。在场景中新建两个 Plane,分别作为遮挡面的反面与正面,调整位置,便于查看各种不同情况的效果。
就可以得到对应的效果了。
原理讲解
- 首先对于深度值写入来说,我们不希望加入的遮挡显示影响其他物体的渲染,即在渲染遮挡的部分的时候我们不会写入深度。即,渲染遮挡部分的 Pass ZWrite off
- 我们需要判断片元是否被遮挡,才能进行渲染被遮挡的部分为想要的效果。那我们就需要将 SubShader 队列 尽量设置到后面,只有当其他物体渲染完毕写入深度值后,才可以得知是否需要渲染成被遮挡的样子。即,在被应用的 Shader 的 SubShader Tag{“Queue” = “Geometry+200”},队列尽量设置在后面
- 被遮挡的部分判断我们使用深度值大于缓冲中的深度值,即,ZTest Greater
- 由于我们不希望使用同一个遮挡效果的片元之间有互相的遮挡效果,那么我们就应该将遮挡显示轮廓效果的 Pass 放到正常显示的 Pass 之前,不然的话,先渲染了正常显示的部分,写入了深度值,再渲染遮挡现实效果的部分就会出现这样的效果。
大家可以更改一下 UsePass 的位置,更改一下深度测试、深度写入的开关或者更改一下队列值,会对整体的理解更深刻一些,或者使用 Windows—>Analysis—>FrameDebug 窗口一步步渲染看下效果。
总结
可以看到,其实使用 Shader 也比较方便,只需要自定义一个 Pass,但是不能根据层级筛选,并且需要手动在代码中修改,队列设置也需要在项目中规定好,而不是可视化地设置。